package xj.model.uml;

import static xj.model.uml.UMLModelConstants.ATTR_ASSOCIATION_END_LEFT;
import static xj.model.uml.UMLModelConstants.ATTR_ASSOCIATION_END_RIGHT;
import static xj.model.uml.UMLModelConstants.ATTR_ASSOCIATION_TYPE;
import static xj.model.uml.UMLModelConstants.ATTR_NAME;
import static xj.model.uml.UMLModelConstants.ATTR_SUBTYPE;
import static xj.model.uml.UMLModelConstants.ROLE_ASSOCIATION_CLASS;
import xj.meta.ModelElement;

/**
 * UML associations For directed associations (generalization, dependency, etc)
 * Left-end: tail Right-end: head For aggregation, composition Left-end:
 * container (diamond) Right-end: component (no diamond)
 */
public class UMLAssociation 
  extends ConstrainedAssociationElement {

  public enum AssociationType {
      Default, 

      Generalization, 
      Realization, 
      MixIn, 

      Aggregation, 
      Composition, 
      Association, 

      Dependency, 

      Delegation,

      Containment,

      RequiredInterface,
      ProvidedInterface,

      UseCaseDependency, 

      Link, 
      Notes_Link,
  }

  public UMLAssociation() {
    this(null, AssociationType.Default, null, null, null, null);
  }

  public UMLAssociation(AssociationType type) {
    this(null, type, null, hasEnds(type) ? new UMLAssociationEnd() : null,
	 null, hasEnds(type) ? new UMLAssociationEnd() : null);
  }

  public UMLAssociation(AssociationType type,
			ModelElement left,
			ModelElement right) {
    this(null, type, left, right);
  }

  public UMLAssociation(String name, 
			AssociationType type, 
			ModelElement left,
			ModelElement right) {
    super(left, right);
    setName(name);
    setType(type);
    setAssociationEndLeft(hasEnds(type) ? new UMLAssociationEnd() : null);
    setAssociationEndRight(hasEnds(type) ? new UMLAssociationEnd() : null);
    if (type == AssociationType.Composition) {
      setLeftAggregation(UMLAssociationEnd.AggregationKind.Composite);
    } else if (type == AssociationType.Aggregation) {
      setLeftAggregation(UMLAssociationEnd.AggregationKind.Shared);
    }
  }

  public UMLAssociation(String name, AssociationType type, 
			ModelElement left, UMLAssociationEnd leftEnd, 
			ModelElement right, UMLAssociationEnd rightEnd) {
    super(left, right);
    setName(name);
    setType(type);
    // setLeftElement(left);
    // setRightElement(right);
    setAssociationEndLeft(leftEnd);
    setAssociationEndRight(rightEnd);
    if (type == AssociationType.Composition) {
      setLeftAggregation(UMLAssociationEnd.AggregationKind.Composite);
    } else if (type == AssociationType.Aggregation) {
      setLeftAggregation(UMLAssociationEnd.AggregationKind.Shared);
    }
  }

  public AssociationType getType() {
    return type;
  }

  public void setType(AssociationType type) {
    this.type = type;
    setAttribute(ATTR_ASSOCIATION_TYPE, type);
  }

  public String getName() {
    return name;
  }

  public void setName(String name) {
    this.name = name;
    setAttribute(ATTR_NAME, name);
  }

  public String getSubtype() {
    return subtype;
  }

  public void setSubtype(String subtype) {
    this.subtype = subtype;
    setAttribute(ATTR_SUBTYPE, subtype);
  }

  public UMLAssociationEnd getAssociationEndLeft() {
    return leftEnd;
  }

  public void setAssociationEndLeft(UMLAssociationEnd e) {
    if (leftEnd != null) {
      removeOwnedElement(leftEnd);
    }
    leftEnd = e;
    if (e != null) {
      addOwnedElement(e, ATTR_ASSOCIATION_END_LEFT);
    }
  }

  public UMLAssociationEnd getAssociationEndRight() {
    return rightEnd;
  }

  public void setAssociationEndRight(UMLAssociationEnd e) {
    if (rightEnd != null) {
      removeOwnedElement(rightEnd);
    }
    rightEnd = e;
    if (e != null) {
      addOwnedElement(e, ATTR_ASSOCIATION_END_RIGHT);
    }
  }

  public void addUMLAssociationEnd(UMLAssociationEnd e) {
    if (e != null) {
      String role = e.getRole();
      if (ATTR_ASSOCIATION_END_RIGHT.equals(role)) {
	setAssociationEndRight(e);
      } else if (ATTR_ASSOCIATION_END_LEFT.equals(role)) {
	setAssociationEndLeft(e);
      }
    }
  }

  public String getLeftRole() {
    if (leftEnd != null) {
      return leftEnd.getRoleName();
    }
    return null;
  }

  public void setLeftRole(String role) {
    if (leftEnd == null && hasEnds(type)) {
      setAssociationEndLeft(new UMLAssociationEnd());
    }
    if (leftEnd != null) {
      leftEnd.setRoleName(role);
    }
  }

  public String getRightRole() {
    if (rightEnd != null) {
      return rightEnd.getRoleName();
    }
    return null;
  }

  public void setRightRole(String role) {
    if (rightEnd == null && hasEnds(type)) {
      setAssociationEndRight(new UMLAssociationEnd());
    }
    if (rightEnd != null) {
      rightEnd.setRoleName(role);
    }
  }

  public Multiplicity getLeftMultiplicity() {
    if (leftEnd != null) {
      return leftEnd.getMultiplicity();
    }
    return null;
  }

  public void setLeftMultiplicity(Multiplicity multiplicity) {
    if (leftEnd == null && hasEnds(type)) {
      setAssociationEndLeft(new UMLAssociationEnd());
    }
    if (leftEnd != null) {
      leftEnd.setMultiplicity(multiplicity);
    }
  }

  public Multiplicity getRightMultiplicity() {
    if (rightEnd != null) {
      return rightEnd.getMultiplicity();
    }
    return null;
  }

  public void setRightMultiplicity(Multiplicity multiplicity) {
    if (rightEnd == null && hasEnds(type)) {
      setAssociationEndRight(new UMLAssociationEnd());
    }
    if (rightEnd != null) {
      rightEnd.setMultiplicity(multiplicity);
    }
  }

  public CollectionType getLeftCollectionType() {
    if (leftEnd != null) {
      return leftEnd.getCollectionType();
    }
    return null;
  }

  public void setLeftCollectionType(CollectionType collectionType) {
    if (leftEnd == null && hasEnds(type)) {
      setAssociationEndLeft(new UMLAssociationEnd());
    }
    if (leftEnd != null) {
      leftEnd.setCollectionType(collectionType);
    }
  }

  public CollectionType getRightCollectionType() {
    if (rightEnd != null) {
      return rightEnd.getCollectionType();
    }
    return null;
  }

  public void setRightCollectionType(CollectionType collectionType) {
    if (rightEnd == null && hasEnds(type)) {
      setAssociationEndRight(new UMLAssociationEnd());
    }
    if (rightEnd != null) {
      rightEnd.setCollectionType(collectionType);
    }
  }

  public Changeability getLeftChangeability() {
    if (leftEnd != null) {
      return leftEnd.getChangeability();
    }
    return null;
  }

  public void setLeftChangeability(Changeability changeability) {
    if (leftEnd == null && hasEnds(type)) {
      setAssociationEndLeft(new UMLAssociationEnd());
    }
    if (leftEnd != null) {
      leftEnd.setChangeability(changeability);
    }
  }

  public Changeability getRightChangeability() {
    if (rightEnd != null) {
      return rightEnd.getChangeability();
    }
    return null;
  }

  public void setRightChangeability(Changeability changeability) {
    if (rightEnd == null && hasEnds(type)) {
      setAssociationEndRight(new UMLAssociationEnd());
    }
    if (rightEnd != null) {
      rightEnd.setChangeability(changeability);
    }
  }

  public UMLAssociationEnd.AggregationKind getLeftAggregation() {
    if (leftEnd != null) {
      return leftEnd.getAggregation();
    }
    return null;
  }

  public void setLeftAggregation(UMLAssociationEnd.AggregationKind aggregation) {
    if (leftEnd == null && hasEnds(type)) {
      setAssociationEndLeft(new UMLAssociationEnd());
    }
    if (leftEnd != null) {
      leftEnd.setAggregation(aggregation);
    }
  }

  public UMLAssociationEnd.AggregationKind getRightAggregation() {
    if (rightEnd != null) {
      return rightEnd.getAggregation();
    }
    return null;
  }

  public void setRightAggregation(UMLAssociationEnd.AggregationKind aggregation) {
    if (rightEnd == null && hasEnds(type)) {
      setAssociationEndRight(new UMLAssociationEnd());
    }
    if (rightEnd != null) {
      rightEnd.setAggregation(aggregation);
    }
  }

  public Navigability getNavigabilityToLeft() {
    if (leftEnd != null) {
      return leftEnd.getNavigability();
    }
    return null;
  }

  public void setNavigabilityToLeft(Navigability b) {
    if (leftEnd == null && hasEnds(type)) {
      setAssociationEndLeft(new UMLAssociationEnd());
    }
    if (leftEnd != null) {
      leftEnd.setNavigability(b);
    }
  }

  public Navigability getNavigabilityToRight() {
    if (rightEnd != null) {
      return rightEnd.getNavigability();
    }
    return null;
  }

  public void setNavigabilityToRight(Navigability b) {
    if (rightEnd == null && hasEnds(type)) {
      setAssociationEndRight(new UMLAssociationEnd());
    }
    if (rightEnd != null) {
      rightEnd.setNavigability(b);
    }
  }

  public boolean isOrderedLeft() {
    if (leftEnd != null) {
      return leftEnd.isOrdered();
    }
    return false;
  }

  public void setOrderedLeft(boolean b) {
    if (leftEnd == null && hasEnds(type)) {
      setAssociationEndLeft(new UMLAssociationEnd());
    }
    if (leftEnd != null) {
      leftEnd.setOrdered(b);
    }
  }

  public boolean isOrderedRight() {
    if (rightEnd != null) {
      return rightEnd.isOrdered();
    }
    return false;
  }

  public void setOrderedRight(boolean b) {
    if (rightEnd == null && hasEnds(type)) {
      setAssociationEndRight(new UMLAssociationEnd());
    }
    if (rightEnd != null) {
      rightEnd.setOrdered(b);
    }
  }

  public boolean isUniqueLeft() {
    if (leftEnd != null) {
      return leftEnd.isUnique();
    }
    return false;
  }

  public void setUniqueLeft(boolean b) {
    if (leftEnd == null && hasEnds(type)) {
      setAssociationEndLeft(new UMLAssociationEnd());
    }
    if (leftEnd != null) {
      leftEnd.setUnique(b);
    }
  }

  public boolean isUniqueRight() {
    if (rightEnd != null) {
      return rightEnd.isUnique();
    }
    return false;
  }

  public void setUniqueRight(boolean b) {
    if (rightEnd == null && hasEnds(type)) {
      setAssociationEndRight(new UMLAssociationEnd());
    }
    if (rightEnd != null) {
      rightEnd.setUnique(b);
    }
  }

  public UMLClass getAssociationClass() {
    return associationClass;
  }

  public void setAssociationClass(UMLClass aClass) {
    if (associationClass != null) {
      removeOwnedElement(associationClass);
    }
    associationClass = aClass;
    if (aClass != null) {
      addOwnedElement(aClass, ROLE_ASSOCIATION_CLASS);
    }
  }

  @Override
  public void swapEnds() { 
    super.swapEnds();
    UMLAssociationEnd e = leftEnd;
    leftEnd = rightEnd;
    rightEnd = e; 
  }

  @Override
  public String toString() {
    return "UMLAssociation name=" + name + " type=" + type + " right=" + 
      getAssociationEndRight() + " left=" + getAssociationEndLeft();
  }

  @Override
  public Object clone() throws CloneNotSupportedException {
    UMLAssociation a = (UMLAssociation) super.clone();
    a.setType(type);
    a.setName(name);
    a.setAssociationEndLeft((leftEnd != null) ? (UMLAssociationEnd) leftEnd.clone() : null);
    a.setAssociationEndRight((rightEnd != null) ? (UMLAssociationEnd) rightEnd.clone() : null);

    if (debug) {
      /*
       * System.out.println("UMLAssociation.clone() this.navRight=" +
       * isNavigabilityToRight() + " this.navLeft=" + isNavigabilityToLeft());
       * System.out.println("UMLAssociation.clone() navRight=" +
       * a.isNavigabilityToRight() + " navLeft=" + a.isNavigabilityToLeft());
       */
      System.out.println("UMLAssociation.clone() id=" + getId() + 
			 " a.id=" + a.getId() + 
			 " a.right=" + a.getAssociationEndRight() + 
			 " a.left=" + a.getAssociationEndLeft());
    }

    return a;
  }

  protected static boolean hasEnds(AssociationType type) {
    return (type == AssociationType.Aggregation || 
	    type == AssociationType.Composition || 
	    type == AssociationType.Association ||
	    type == AssociationType.Delegation);
  }

  protected AssociationType type;

  protected String name;

  protected UMLAssociationEnd leftEnd, rightEnd;

  protected String subtype;

  protected UMLClass associationClass = null;

  protected static final boolean debug = false;

}
